
/* License Notice:
**
** This program is free software: you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation, either version 3 of the License, or
**    (at your option) any later version.
** This program is distributed in the hope that it will be useful,
**   but WITHOUT ANY WARRANTY; without even the implied warranty of
**   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
**   GNU General Public License for more details.
** You should have received a copy of the GNU General Public License
**   along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/**
 * @file window_manager.cpp
 * @author TooOld2Rock'nRoll
 * @see window_manager.hpp
 */
/*---- Includes ----*/
#include "graphics/window_manager.hpp"

#include <fmt/core.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_hints.h>

#include "toolbox/debug.h"


/*---- Global Variables ----*/


/*---- Local Functions ----*/


/*---- Methods Implementation ----*/
/**
 * @brief WindowManager Constructor.
 */
WindowManager::WindowManager ()
{
    _debug (INFO, "::%s () <--The WindowManager singleton has been created!", __FUNCTION__);
}//end Constructor

/**
 * @brief Access the singleton instance.
 *
 * @return Reference to the WindowManager singleton.
 */
WindowManager& WindowManager::instance ()
{
    static WindowManager _instance;

    _debug (TRACE, "::%s ()", __FUNCTION__);

    return _instance;
}//End instance ()

/**
 * @brief WindowManager Destructor.
 */
WindowManager::~WindowManager ()
{
    _debug (INFO, "::%s () <--The WindowManager singleton will be destroyed!", __FUNCTION__);

    if (this->p_sdlWindow != nullptr)
        SDL_DestroyWindow (this->p_sdlWindow);

    SDL_Quit ();
}//End Destructor


/**
 * @brief Setup the necessary links to make the API work and create the objects to keep it running.
 *
 * @param window_title[] - string to use as window decoration title.
 * @param target_display - in which display will the window open (defaults to 0 if target don't exist).
 * @param window_flags - 'or' list of behaviors to apply to the window.
 */
void WindowManager::init (const char window_title[], unsigned target_display, SDL_WindowFlags window_flags)
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    if (!window_title)
        throw std::invalid_argument (fmt::format ("{0}:{1} - No window title provided!", __LINE__, __FILE__));
    else if (this->p_sdlWindow)
    {
        _debug (ERROR, "Program window already created, don't call init() twice!");
        return;
    }//end if (this->p_sdlWindow)

    /* Mix presets that helps SDL to make decisions about the running environment */
    SDL_SetHint (SDL_HINT_APP_NAME, window_title);
    SDL_SetHint (SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "1");
    SDL_SetHint (SDL_HINT_AUDIO_DEVICE_STREAM_ROLE, "game");
    SDL_SetHint (SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, "ArcadeFighter is running a game!");
    SDL_SetHint (SDL_HINT_WINDOWS_DPI_SCALING, "1");
    SDL_SetHint (SDL_HINT_AUDIO_INCLUDE_MONITORS, "1");
    /* Setting up OpenGL version and profile details for context creation */
    SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute (SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute (SDL_GL_CONTEXT_MAJOR_VERSION, OPENGL_TARGET_MAJOR_VERSION);
    SDL_GL_SetAttribute (SDL_GL_CONTEXT_MINOR_VERSION, OPENGL_TARGET_MINOR_VERSION);
    /* Enable multi sampling for a nice anti-aliased effect */
    SDL_GL_SetAttribute (SDL_GL_MULTISAMPLEBUFFERS, 1);
    SDL_GL_SetAttribute (SDL_GL_MULTISAMPLESAMPLES, 4);

    if (SDL_Init (SDL_INIT_VIDEO) < 0)
        throw std::runtime_error (fmt::format ("{0}:{1} - Failed to init SDL, error = {2}!", __LINE__, __FILE__, SDL_GetError()));

    /* Makes (some of) OpenGl work with threads (MUST be set after init!) */
    SDL_GL_SetAttribute (SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);

    _debug (INFO, "Native window API initiated successfully.");

    //be sure the display exists before trying to create the window.
    if ((int)target_display >= SDL_GetNumVideoDisplays ())
        target_display = 0;

    this->p_sdlWindow = SDL_CreateWindow (window_title,
                                          SDL_WINDOWPOS_CENTERED_DISPLAY(target_display),
                                          SDL_WINDOWPOS_CENTERED_DISPLAY(target_display),
                                          START_WINDOW_WIDTH, START_WINDOW_HEIGHT,
                                          SDL_WINDOW_OPENGL | window_flags);

    //gambiarra!!! in case resize flags were used to crate the window, we need to wait for it to take effect
    SDL_WaitEventTimeout (NULL, 1000);
    SDL_GL_GetDrawableSize (this->p_sdlWindow, (int *)&this->u_win_width, (int *)&this->u_win_height);
    _debug (INFO, "Final window draw area is %u/%upx", this->u_win_width, this->u_win_height);
}//End init ()

